﻿using IM.Easy.Core.Services;
using IM.Easy.Core.Utils;
using IM.Easy.Entity.Scheduled;
using IM.Easy.ScheduledServices.IServices;
using IM.Easy.ScheduledServices.Jobs;
using Quartz;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static Quartz.Logging.OperationName;

namespace IM.Easy.ScheduledServices.Services
{
    /// <summary>
    /// 调度器服务
    /// </summary>
    public class SchedulerService : ISchedulerService
    {
        public IScheduler Scheduler { get; }

        public SchedulerService(IScheduler scheduler)
        {
            Scheduler = scheduler;
        }


        /// <summary>
        /// 新增一个任务计划
        /// </summary>
        public async Task NewScheduler(ScheduledJob job)
        {
            var instance = BuildJob(job);
            await Scheduler.AddJob(instance, replace: true);
        }

        /// <summary>
        /// 移除一个任务计划
        /// </summary>
        /// <returns></returns>
        public Task<bool> RemoveScheduler(string schedulerId)
        {
            var flag = Scheduler.DeleteJob(new JobKey(schedulerId));
            return flag;
        }

        /// <summary>
        /// 批量移除任务计划
        /// </summary>
        /// <param name="schedulerIds"></param>
        /// <returns></returns>
        public Task<bool> RemoveSchedulers(params string[] schedulerIds)
        {
            var jobKeys = schedulerIds.Select(id => new JobKey(id)).ToList();
            var flag = Scheduler.DeleteJobs(jobKeys);
            return flag;
        }

        /// <summary>
        /// 启动一个任务
        /// </summary>
        /// <param name="trigger"></param>
        /// <returns>返回首次执行时间</returns>
        public Task<DateTimeOffset> StartScheduler(JobTrigger trigger)
        {
            var jobKey = new JobKey(trigger.JobId.ToString());
            ITrigger quartzTrigger = BuildTrigger(jobKey, trigger);
            var firstRuntime = Scheduler.ScheduleJob(quartzTrigger);
            return firstRuntime;
        }

        /// <summary>
        /// 暂停一个任务(本质上是暂停触发器)
        /// </summary>
        /// <param name="schedulerId"></param>
        /// <returns></returns>
        public Task PauseScheduler(string triggerId)
        {
            var triggerKey = new TriggerKey(triggerId);
            return Scheduler.PauseTrigger(triggerKey);
        }

        /// <summary>
        /// 恢复一个任务(本质上是恢复触发器)
        /// </summary>
        /// <param name="schedulerId"></param>
        /// <returns></returns>
        public Task ResumeScheduler(string triggerId)
        {
            var triggerKey = new TriggerKey(triggerId);
            return Scheduler.ResumeTrigger(triggerKey);
        }

        /// <summary>
        /// 执行一次任务
        /// </summary>
        /// <returns></returns>
        public Task TriggerOnce(string schedulerId)
        {
            var jobKey = new JobKey(schedulerId);
            var jobDataMap = new JobDataMap();
            jobDataMap.Put("single", true);
            return Scheduler.TriggerJob(jobKey, jobDataMap);
        }

        #region private methods

        private ITrigger BuildTrigger(JobKey jobKey, JobTrigger trigger)
        {
            TriggerBuilder builder = TriggerBuilder.Create()
                .ForJob(jobKey)
                .WithIdentity($"{trigger.Id}");


            #region 时间限制

            if (trigger.StartMode == TriggerStartMode.Scheduled)
                builder.StartAt(trigger.EffictStartTime.Value);

            if (trigger.EffictEndTime.HasValue)
                builder.EndAt(trigger.EffictEndTime.Value);
            #endregion

            if (string.Equals(trigger.Type, "cron", StringComparison.OrdinalIgnoreCase))
            {
                builder.WithCronSchedule(trigger.Cron, s => s.WithMisfireHandlingInstructionDoNothing());
            }
            else if (string.Equals(trigger.Type, "simple", StringComparison.OrdinalIgnoreCase))
            {
                builder.WithSimpleSchedule(s =>
                s.WithIntervalInSeconds(trigger.Interval)
                 .WithRepeatCount(trigger.Repeat)
                 .WithMisfireHandlingInstructionIgnoreMisfires());
                //MisfireInstruction.SimpleTrigger.RescheduleNextWithRemainingCount
            }
            else
            {
                return null; // 无效触发器配置
            }

            return builder.Build();
        }


        private IJobDetail BuildJob(ScheduledJob job)
        {
            var jobKey = new JobKey(job.Id.ToString());

            var jobType = typeof(RemoteJob);
            if (job.Extend.Type == JobType.LocalProgram) jobType = Type.GetType(job.Extend.JobType);

            if (jobType == null) ExceptionUtils.ThrowSystemException($"未能找到相关job类型:{job.Extend.Type}");

            var builder = JobBuilder.Create(jobType);

            builder = builder
                   .WithIdentity(jobKey)
                   .StoreDurably();
            var parameters = GetJobParameters(job);
            var jobData = new JobDataMap(parameters);
            if (!jobData.IsEmpty)
            {
                builder.UsingJobData(jobData);
            }
            var jobDetail = builder.Build();

            return jobDetail;
        }

        /// <summary>
        /// 添加 job.Extend.Params 到 dataMap（添加 "param_" 前缀）。
        /// </summary>
        /// <param name="dataMap">目标 JobDataMap</param>
        /// <param name="job">ScheduledJob 对象</param>
        private Dictionary<string, string> GetJobParameters(ScheduledJob job)
        {
            var map = new Dictionary<string, string>();

            //请求地址
            if (!string.IsNullOrEmpty(job.Extend.Remote))
            {
                map["remote_url"] = job.Extend.Remote;
                map["remote_method"] = job.Extend.Method;
            }

            //请求参数  &&  额外参数
            if (job.Extend?.Params != null)
            {
                foreach (var param in job.Extend.Params)
                {
                    var key = $"param_{param.Key}";
                    map[key] = param.Key;
                }
            }

            return map;
        }
        #endregion

    }
}
